home *** CD-ROM | disk | FTP | other *** search
-
- /* Generated by Interface Builder */
-
- #import "Plot.h"
- #import "NXYView.h" /* only for initializeLegendBox */
- #import <appkit/Matrix.h>
- #import <appkit/Cell.h>
- #import <math.h>
- #import <appkit/OpenPanel.h>
- #import <appkit/publicWraps.h> // For NXBeep (from 1.7)
-
- #define ALLOCSIZE 2048 /* used in malloc in readData */
-
- //Added for 1.7 functions
- int beepError=0;
-
- // Added from nxyplot 1.7
- /* The following routines are in auxil.m: */
- extern void computeNiceLinInc(float *, float *, float *);
- extern void computeNiceLogInc(float *, float *, float *);
-
-
-
- @implementation Plot
-
- - (NXCoord *)xdata { return x;}
- - (NXCoord **)ydata { return y;}
-
- - (int)nPoints { return npoints;}
- - (int)nCurves { return ncurves;}
-
- //- (const char *)provideXtitle
- // {return [xTitle stringValueAt:0];}
- // {return [canvas xaxisLabel];}
- //- (const char *)provideYtitle
- // {return [yTitle stringValueAt:0];}
- // {return [canvas yaxisLabel];}
- //- (const char *)provideMaintitle
- // {return [mainTitle stringValueAt:0];}
- //{return [canvas mainTitle];}
-
- - (BOOL) shouldDrawGrid
- {
- // if ( [gridOnOff state] ) return YES;
- if ( [canvas gridState] ) return YES;
- else return NO;
- }
-
- - (BOOL) shouldDrawBox
- {
- // if ( [borderBoxOnOff state] ) return YES;
- if ( [canvas borderState] ) return YES;
- else return NO;
- }
-
-
- - (BOOL) xaxisLog
- {
- // if ( [xLinLog state] ) return YES;
- if([canvas xLinLogState]) return YES;
- else return NO;
- }
-
- - forceXaxisLinear
- {
- // [xLinLog setState:0];
- // [xLinLog display];
- [canvas setXLinLogState:LINEAR];
- return self;
- }
-
- - (BOOL) yaxisLog
- {
- // if ( [yLinLog state] ) return YES;
- if([canvas yLinLogState]) return YES;
- else return NO;
- }
-
- - forceYaxisLinear
- {
- // [yLinLog setState:0];
- // [yLinLog display];
- [canvas setYLinLogState:LINEAR];
- return self;
- }
-
- - (int)providelinestyle:(int)aCurve
- {
- return 0; /* for safety */
- }
-
- - (int)providesymbolstyle:(int)aCurve
- {
- return 0; /* for safety */
- }
-
- - (int)providesymbolsize{ return 0;}
- - (int)providelinethickness{ return 0;}
-
- //- (float)provideXmin {return [xMin floatValueAt:0];}
- - (float)provideXmin {return [canvas xMinValue];}
- //- (float)provideXmax {return [xMax floatValueAt:0];}
- - (float)provideXmax {return [canvas xMaxValue];}
- //- (float)provideXinc {return [xInc floatValueAt:0];}
- - (float)provideXinc {return [canvas xIncValue];}
- //- (float)provideYmin {return [yMin floatValueAt:0];}
- - (float)provideYmin {return [canvas yMinValue];}
- //- (float)provideYmax {return [yMax floatValueAt:0];}
- - (float)provideYmax {return [canvas yMaxValue];}
- //- (float)provideYinc {return [yInc floatValueAt:0];}
- - (float)provideYinc {return [canvas yIncValue];}
-
- //- resetXmin:(float)aNum { [xMin setFloatValue:aNum at:0]; return self; }
- - resetXmin:(float)aNum { [canvas setXminValue:aNum]; return self; }
- //- resetXmax:(float)aNum { [xMax setFloatValue:aNum at:0]; return self; }
- - resetXmax:(float)aNum { [canvas setXmaxValue:aNum]; return self; }
- //- resetXinc:(float)aNum { [xInc setFloatValue:aNum at:0]; return self; }
- - resetXinc:(float)aNum { [canvas setXincValue:aNum]; return self; }
- //- resetYmin:(float)aNum { [yMin setFloatValue:aNum at:0]; return self; }
- - resetYmin:(float)aNum { [canvas setYminValue:aNum]; return self; }
- //- resetYmax:(float)aNum { [yMax setFloatValue:aNum at:0]; return self; }
- - resetYmax:(float)aNum { [canvas setYmaxValue:aNum]; return self; }
- //- resetYinc:(float)aNum { [yInc setFloatValue:aNum at:0]; return self; }
- - resetYinc:(float)aNum { [canvas setYincValue:aNum]; return self; }
-
- - drawPlot:sender
- {
- // if (!x) return self;
-
- [self sanityCheck]; /* disallow various bad parameters */
- [canvas display];
- return self;
- }
-
- // Allocate enough memory and read the data points
- /*
- * WARNING: This code will go into an infinite loop if there is illegal
- * crud in the data file (such as a comma, for example). The culprit is
- * the "fscanf" below; we will put up with this for now, but we should
- * make this more robust.
- */
- - (int) readDataFromFile:(FILE *)aDataStream
- {
- BOOL inword = NO;
- char c;
- int j, size = ALLOCSIZE;
- // int oldncurves = ncurves; /* for linestyle matrix column deleting */
-
- NXPing(); /* Probably not needed now--was for Plot Button */
-
- /* First, free x and y here if there's already data in them */
- if (x) {
- free( (void *)x );
- for (j = 0; j < ncurves; j++) {
- free( (void *)(*(y+j)) );
- }
- free( (void *)y );
- }
-
- /* Figure out the number of curves in the file by reading characters */
- /* until a newline is encountered; the number of curves is one less */
- /* than the number of words found. */
- /* For now we assume the input file is an ascii file. */
- ncurves = -1;
- while (1) {
- c = getc(aDataStream);
- if (c == '\n') {
- break; /* breaks out of while loop */
- }
- if ((inword==NO) && !(c==' ' || c=='\t')) {
- ncurves++;
- inword = YES;
- }
- if ((inword==YES) && (c==' ' || c=='\t')) {
- inword = NO;
- }
- }
- if (ncurves == -1) { /* couldn't find "\n", give up */
- return 0;
- }
-
- /* Now read the data into memory */
- rewind(aDataStream);
-
- y = (NXCoord **)malloc( ncurves * sizeof(NXCoord *) );
- x = (NXCoord *)malloc( size * sizeof(NXCoord) );
- for (j = 0; j < ncurves; j++) {
- *(y+j) = (NXCoord *)malloc( size * sizeof(NXCoord) );
- }
- npoints = 0;
- while(1) {
- if( (fscanf(aDataStream, "%f", x+npoints)) == EOF ) {
- break; /* breaks out of the while loop */
- }
- for (j = 0; j < ncurves; j++) {
- fscanf(aDataStream, "%f", *(y+j)+npoints);
- }
- npoints++;
- if (npoints == size) { /* get more memory */
- size += ALLOCSIZE;
- x = (NXCoord *)realloc(x, size * sizeof(NXCoord));
- for (j = 0; j < ncurves; j++) {
- *(y+j) = (NXCoord *)realloc( *(y+j), size * sizeof(NXCoord) );
- }
- }
- }
-
- return npoints;
- }
-
- // Following was added by cwf. It is just the above method modified for a NXStream
- // This should be replaced by the equivalent code from nxyplot1.7
- /*
- * WARNING: This code will go into an infinite loop if there is illegal
- * crud in the data file (such as a comma, for example). The culprit is
- * the "fscanf" below; we will put up with this for now, but we should
- * make this more robust.
- */
- - (int) readDataFromStream:(NXStream *)aDataStream;
- {
- BOOL inword = NO;
- char c;
- int j, size = ALLOCSIZE;
- // int oldncurves = ncurves; /* for linestyle matrix column deleting */
-
- NXPing(); /* Probably not needed now--was for Plot Button */
-
- /* First, free x and y here if there's already data in them */
- if (x) {
- free( (void *)x );
- for (j = 0; j < ncurves; j++) {
- free( (void *)(*(y+j)) );
- }
- free( (void *)y );
- }
-
- /* Figure out the number of curves in the file by reading characters */
- /* until a newline is encountered; the number of curves is one less */
- /* than the number of words found. */
- /* For now we assume the input file is an ascii file. */
- ncurves = -1;
- while (1) {
- c = NXGetc(aDataStream);
- if (c == '\n') {
- break; /* breaks out of while loop */
- }
- if ((inword==NO) && !(c==' ' || c=='\t')) {
- ncurves++;
- inword = YES;
- }
- if ((inword==YES) && (c==' ' || c=='\t')) {
- inword = NO;
- }
- }
- if (ncurves == -1) { /* couldn't find "\n", give up */
- return 0;
- }
-
- /* Now read the data into memory */
- NXSeek(aDataStream,0L,NX_FROMSTART);
-
- y = (NXCoord **)malloc( ncurves * sizeof(NXCoord *) );
- x = (NXCoord *)malloc( size * sizeof(NXCoord) );
- for (j = 0; j < ncurves; j++) {
- *(y+j) = (NXCoord *)malloc( size * sizeof(NXCoord) );
- }
- npoints = 0;
- while(1) {
- if( (NXScanf(aDataStream, "%f", x+npoints)) == EOF ) {
- break; /* breaks out of the while loop */
- }
- for (j = 0; j < ncurves; j++) {
- NXScanf(aDataStream, "%f", *(y+j)+npoints);
- }
- npoints++;
- if (npoints == size) { /* get more memory */
- size += ALLOCSIZE;
- x = (NXCoord *)realloc(x, size * sizeof(NXCoord));
- for (j = 0; j < ncurves; j++) {
- *(y+j) = (NXCoord *)realloc( *(y+j), size * sizeof(NXCoord) );
- }
- }
- }
-
- return npoints;
- }
-
-
- /* Might want to make sure INLINE_MATH is defined when math.h is included */
-
- - checkLinLog
- {
- /* Check x and y axes -- do a heuristic test for log/lin.
- * The test employed comes from M. Merriam and xyplot.
- */
- double scale, linsum, logsum;
- register double tmp;
- int i, j;
-
- /* First test x axis. */
- if (datamax.x > 0.0 && datamin.x > 0.0 && datamax.x != datamin.x) {
- scale = fabs( (double)datamax.x - (double)datamin.x );
- linsum = 0.0;
- for (j=1; j<npoints; j++) {
- tmp = ( (double)x[j]-(double)x[j-1] )/(double)scale; /* avoid overflow */
- linsum += tmp*tmp;
- }
- scale = log10( (double)datamax.x/(double)datamin.x );
- /* what if datamax.x<datamin.x? */
- logsum = 0.0;
- for (i=1; i<npoints; i++) {
- tmp = log10( (double)x[i]/(double)x[i-1] ) / scale;
- logsum += tmp*tmp;
- }
- if (linsum < logsum) {
- // [xLinLog setState:0]; /* linear axis */
- [canvas setXLinLogState:LINEAR];
- }
- else {
- // [xLinLog setState:1]; /* logarithmic axis */
- [canvas setXLinLogState:LOG];
- }
- }
- else {
- // [xLinLog setState:0]; /* linear */
- [canvas setXLinLogState:LINEAR];
- }
- /* Now test y axis */
- if (datamax.y > 0.0 && datamin.y > 0.0 && datamax.y != datamin.y) {
- scale = fabs( (double)datamax.y - (double)datamin.y );
- linsum = 0.0;
- for (j=0; j<ncurves; j++) {
- for (i=1; i<npoints; i++) {
- tmp = ( (double)*(*(y+j)+i) - (double)*(*(y+j)+i-1) )
- / scale; /* avoid overflow */
- linsum += tmp*tmp;
- }
- }
- scale = log10((double)datamax.y/(double)datamin.y);
- /* what if datamax.y<datamin.y? */
- logsum = 0.0;
- for (j=0; j<ncurves; j++) {
- for (i=1; i<npoints;i++) {
- tmp = log10( (double)*(*(y+j)+i)/(double)*(*(y+j)+i-1) ) / scale;
- logsum += tmp*tmp;
- }
- }
- if (linsum < logsum) {
- // [yLinLog setState:0]; /* linear axis */
- [canvas setYLinLogState:LINEAR];
- }
- else {
- // [yLinLog setState:1]; /* logarithmic axis */
- [canvas setYLinLogState:LOG];
- }
- }
- else {
- // [yLinLog setState:0]; /* linear */
- [canvas setYLinLogState:LINEAR];
- }
- // [xLinLog display];
- // [yLinLog display];
- return self;
- }
-
- // Go through the data set and find values for datamin.x,
- // datamax.x, datamin.y, datamax.y
- - findMinAndMax
- {
- if (x) {
- int i, j;
- datamin.x = datamax.x = x[0];
- for (i = 1; i < npoints; i++) {
- if (x[i] < datamin.x)
- datamin.x = x[i];
- else if (x[i] > datamax.x)
- datamax.x = x[i];
- }
- datamin.y = datamax.y = *(*(y+0)+0);
- for (j = 0; j < ncurves; j++) {
- for (i = 0; i < npoints; i++) {
- if (*(*(y+j)+i) < datamin.y)
- datamin.y = *(*(y+j)+i);
- else if (*(*(y+j)+i) > datamax.y)
- datamax.y = *(*(y+j)+i);
- }
- }
- }
- // reset min and max here (may want to change the placement of this)
- // [self resetXmin:datamin.x];
- // [self resetXmax:datamax.x];
- // [self resetXinc:(datamax.x - datamin.x)/5.0];
- // [self resetYmin:datamin.y];
- // [self resetYmax:datamax.y];
- // [self resetYinc:(datamax.y - datamin.y)/5.0];
-
- return self;
- }
-
- // Added from nxyplot 1.7
- // Get pleasing values for the min, max, and increments
- - niceMinMaxInc
- {
- float fmin, fmax, finc;
-
- fmin = datamin.x;
- fmax = datamax.x;
- if ([self xaxisLog] ) {
- computeNiceLogInc(&fmin, &fmax, &finc);
- }
- else {
- computeNiceLinInc(&fmin, &fmax, &finc);
- }
- [self resetXmin:fmin];
- [self resetXmax:fmax];
- [self resetXinc:finc];
-
- fmin = datamin.y;
- fmax = datamax.y;
- if ([self yaxisLog] ) {
- computeNiceLogInc(&fmin, &fmax, &finc);
- }
- else {
- computeNiceLinInc(&fmin, &fmax, &finc);
- }
- [self resetYmin:fmin];
- [self resetYmax:fmax];
- [self resetYinc:finc];
- return self;
- }
-
- //Added from nxyplot 1.7 (Beep code "removed" for now)
- // We make the plotParam object responsible for checking parameters
- // before the PlotView object is called. Thus the PlotView object can
- // assume the parameters are OK, and it doesn't have to do any checking.
- // Things to check: xinc has the same sign as xmax-xmin (same for y);
- // no negative data if log plot requested on x or y axis; there won't be
- // too many tic marks requested.
- - sanityCheck
- {
- float xinc = [self provideXinc];
- float xmax = [self provideXmax], xmin = [self provideXmin];
- float yinc = [self provideYinc];
- float ymax = [self provideYmax], ymin = [self provideYmin];
- int nticmarks;
-
- /* First check: no nonpositive data if logarithmic axis */
- if ( [self xaxisLog] ) {
- if (datamin.x <= 0.0 || xmin <= 0.0 || xmax <= 0.0) {
- // [xLinLog setState:0]; /* back to linear */
- [canvas setXLinLogState:0]; //Changes above line since there is no control panel
- NXBeep(); /* audible alert */
- beepError = 1;
- }
- }
- if ( [self yaxisLog] ) {
- if (datamin.y <= 0.0 || ymin <= 0.0 || ymax <= 0.0) {
- // [yLinLog setState:0]; /* back to linear */
- [canvas setYLinLogState:0]; //Changes above line since there is no control panel
- NXBeep(); /* audible alert */
- beepError = 2;
- }
- }
- /* Second check: xinc has same sign as xmax and xmin */
- if (xinc*(xmax-xmin) <= 0.0) { /* the bad case - avoid infinite loop */
- if (xinc < 0.0) { /* in PlotView:drawSelf */
- xinc = -xinc;
- [self resetXinc:xinc];
- NXBeep();
- beepError = 3;
- }
- if (xmax <= xmin) {
- [self niceMinMaxInc];
- NXBeep(); /* alert */
- beepError = 4;
- }
- }
- /* And similarly for yinc */
- if (yinc*(ymax-ymin) <= 0.0) { /* the bad case - avoid infinite loop */
- if (yinc < 0.0) {
- yinc = -yinc;
- [self resetYinc:yinc];
- NXBeep(); /* alert */
- beepError = 5;
- }
- if (ymax <= ymin) {
- [self niceMinMaxInc];
- NXBeep(); /* alert */
- beepError = 6;
- }
- }
- /* Third check: no more than 100 (say) tic marks on either axis */
- if ( ![self xaxisLog] ) { /* linear axis */
- nticmarks = (int) ((xmax - xmin) / xinc) ;
- if (nticmarks > 100) {
- computeNiceLinInc(&xmin, &xmax, &xinc);
- [self resetXmin:xmin];
- [self resetXmax:xmax];
- [self resetXinc:xinc];
- NXBeep(); /* alert */
- // beepError = 7;
- }
- }
- if ( ![self yaxisLog] ) { /* linear axis */
- nticmarks = (int) ((ymax - ymin) / yinc) ;
- if (nticmarks > 100) {
- computeNiceLinInc(&ymin, &ymax, &yinc);
- [self resetYmin:ymin];
- [self resetYmax:ymax];
- [self resetYinc:yinc];
- NXBeep(); /* alert */
- beepError = 8;
- }
- }
- // This check not implemented in palette
- /* Another check: the axes should be in the frame box. */
- // if ([self shouldDrawAxes] && (ymin>0.0 || ymax<0.0 || xmin>0.0 || xmax<0.0)) {
- // NXBeep();
- // beepError = 14; /* this is only a warning message */
- // }
-
- return self;
- }
-
- // The next method (whyTheBeep) was added from 1.7, but must be implemented by the user
- // Look for the key words "NXBeep()" and "beepError", some have been commented out
- - whyTheBeep:sender
- {
- switch(beepError) {
- case 0:
- NXRunAlertPanel("The Beep Panel", "Press this button if the program beeps\n"
- "and you want to know why", "OK", NULL, NULL);
- break;
- case 1:
- NXRunAlertPanel("The Beep Happened Because:",
- "x-axis changed from log to linear", "OK", NULL, NULL);
- break;
- case 2:
- NXRunAlertPanel("The Beep Happened Because:",
- "y-axis changed from log to linear", "OK", NULL, NULL);
- break;
- case 3:
- NXRunAlertPanel("The Beep Happened Because:",
- "x-increment was negative\n (I changed it)", "OK", NULL, NULL);
- break;
- case 4:
- NXRunAlertPanel("The Beep Happened Because:",
- "xmax was less than xmin\n (I changed them)", "OK", NULL, NULL);
- break;
- case 5:
- NXRunAlertPanel("The Beep Happened Because:",
- "y-increment was negative\n (I changed it)", "OK", NULL, NULL);
- break;
- case 6:
- NXRunAlertPanel("The Beep Happened Because:",
- "ymax was less than ymin\n (I changed them)", "OK", NULL, NULL);
- break;
- case 7:
- NXRunAlertPanel("The Beep Happened Because:",
- "Too many tic marks would have been on the x-axis\n"
- " (I changed the min, max, and/or increment)",
- "OK", NULL, NULL);
- break;
- case 8:
- NXRunAlertPanel("The Beep Happened Because:",
- "Too many tic marks would have been on the y-axis\n"
- " (I changed the min, max, and/or increment)",
- "OK", NULL, NULL);
- break;
- case 9:
- NXRunAlertPanel("The Beep Happened Because:",
- "x-axis was logarithmic but some datum was negative\n"
- " (I changed x-axis to linear)", "OK", NULL, NULL);
- break;
- case 10:
- NXRunAlertPanel("The Beep Happened Because:",
- "y-axis was logarithmic but some datum was negative\n"
- " (I changed y-axis to linear)", "OK", NULL, NULL);
- break;
- case 11:
- NXRunAlertPanel("The Beep Happened Because:",
- "tried to set color of a non-existent curve", "OK", NULL, NULL);
- break;
- case 12:
- NXRunAlertPanel("The Beep Happened Because:",
- "box thickness was negative\n"
- " (I changed it to a positive number)", "OK", NULL, NULL);
- break;
- case 13:
- NXRunAlertPanel("The Beep Happened Because:",
- "axis thickness was negative\n"
- " (I changed it to a positive number)", "OK", NULL, NULL);
- break;
- case 14:
- NXRunAlertPanel("The Beep Happened Because:",
- "axis drawing was requested and at least\n"
- "one axis is outside the framing box", "OK", NULL, NULL);
- break;
- }
- return self;
- }
-
- // Use the OpenPanel object to get a filename
- - open:sender
- {
- char const *fileTypes[2] = {0,0}; // this type is all ASCII files (?)
- // char const *fileTypes[2] = {"xyp",0};
- // this would be only files with an xyp extension
- char fname[1024];
-
- id openPanel = [OpenPanel new];
- if ([openPanel runModalForTypes:fileTypes]) {
- strncpy(fname, [openPanel filename], 1024);
- [self openFile:fname];
- }
- return self;
- }
-
- - openFile:(char *)dataFile
- {
- FILE *dataStream;
-
- if ((dataStream = fopen(dataFile, "r")) == NULL) {
- NXRunAlertPanel("Open", "Cannot open %s", "OK", NULL, NULL, dataFile);
- return self;
- }
-
- if ([self readDataFromFile:dataStream] == 0) {
- NXRunAlertPanel("Read", "Couldn't read any data from %s", "OK",
- NULL, NULL, dataFile);
- fclose(dataStream);
- return self;
- }
- fclose(dataStream);
-
- // [canvas initializeLegendBox];
-
- if([canvas autoMaxMinState] == YES)
- {
- // Get Max and Min from data
- [self findMinAndMax];
- [self niceMinMaxInc];
- }
- //else
- // Use values set in Inspector
- // [canvas setMaxMinValues];
-
- /* Check for linear or log on x and y axes */
- // [self checkLinLog];
- if([canvas autoPaperState])
- {
- [self checkLinLog];
- [self niceMinMaxInc];
- }
- [self drawPlot:self];
-
- return self;
- }
-
-
- // The following was added to use a stream for data to plot which has been sent from
- // some controling App--this will(should) be replaced by later nxyplot (1.7) code since
- // it uses streams for all data plots.
- -useDataStreamAndPlot:(NXStream *)dataStream
- {
- [self readDataFromStream:dataStream];
-
- if([canvas autoMaxMinState] == YES)
- {
- [self findMinAndMax];
- [self niceMinMaxInc];
- }
- /* Check for linear or log on x and y axes */
- if([canvas autoPaperState])
- {
- [self checkLinLog];
- [self niceMinMaxInc];
- }
- [self drawPlot:self];
-
- return self;
- }
-
-
- //This archiving is unnecessary for the most part (canvas and ?? need to be archived,
- // the other instance variables could be removed.) Just here on the off chance I figure
- // out how to add a control panel.
- - read:(NXTypedStream*)stream
- /*
- * Unarchives the PlotView. Initializes four of the PlotView's instance variables
- * to the values stored in the stream.
- */
- {
- [super read:stream];
- NXReadTypes(stream, "@", &canvas);
-
- return self;
- }
-
- - write:(NXTypedStream*)stream
- /*
- * Archives the PlotView by writing its important instance variables to the stream.
- */
- {
- [super write:stream];
- NXWriteTypes(stream, "@", &canvas);
-
- return self;
- }
-
-
-
-
- @end
-